package lox

import java.io.BufferedReader
import java.io.File
import java.io.InputStreamReader
import kotlin.system.exitProcess


object Lox {

    private var hadError = false
    private var hadRuntimeError = false

    // todo: 静态
    private val interpreter = Interpreter()

    fun main(args: Array<String>) {
        when {
            args.size > 1 -> {
                println("Usage: lox-kt [script]")
                exitProcess(64)
            }

            args.size == 1 -> runFile(args[0])
            else -> runPrompt()
        }
    }

    private fun runFile(path: String) {
//        val bytes = Path(path).toFile().readBytes()
//        run(bytes.toString())
        File(path).readText(Charsets.UTF_8).run {
            run(this)
        }

        if (hadError) exitProcess(65)
        if (hadRuntimeError) exitProcess(70)
    }

    private fun runPrompt() {
        val input = InputStreamReader(System.`in`)
        val reader = BufferedReader(input)

        print("> ")
        var line: String?
        while (reader.readLine().also { line = it } != null) {
            run(line!!)
            hadError = false
            print("> ")
        }

        reader.close()
    }

    private fun run(source: String) {
        val scanner = Scanner(source)
        val tokens: List<Token> = scanner.scanTokens()


        val parser = Parser(tokens)
        val statements = parser.parse()
        if (hadError) return

        Resolver(interpreter).resolve(statements)
        if (hadError) return

        interpreter.interpret(statements)
    }

    fun error(line: Int, message: String) {
        report(line, "", message)
    }

    fun error(token: Token, message: String) {
        report(token.line, if (token.type == TokenType.EOF) " at end" else " at '${token.lexeme}'", message)
    }

    private fun report(
        line: Int, where: String, message: String
    ) {
        println(
            "[line $line] Error$where: $message"
        )
        hadError = true
    }

    fun runtimeError(error: RuntimeError) {
        System.err.println("${error.msg}\n[line ${error.token.line}]")
        hadRuntimeError = true
    }
}